<?php
/**
*
* Category Model
*
* @package	VirtueMart
* @subpackage Category
* @author jseros, RickG
* @link http://www.virtuemart.net
* @copyright Copyright (c) 2004 - 2010 VirtueMart Team. All rights reserved.
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
* VirtueMart is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* @version $Id: category.php 3095 2011-04-28 17:45:47Z Milbo $
*/

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

// Load the model framework
jimport( 'joomla.application.component.model');


/**
 * Model for product categories
 * @author jseros
 */
class VirtueMartModelCategory extends JModel {

	/**
	 * @var integer Primary key
	 * @access private
	 */
    private $_id;

	/**
	 * @var objectlist Category data
	 * @access private
	 */
    private $_data;

	/**
	 * @var integer Total number of categories in the database
	 * @access private
	 */
	private $_total;

	/**
	 * @var pagination Pagination for country list
	 * @access private
	 */
	private $_pagination;


    /**
     * Constructor for the country model.
     *
     * The category id is read and determined if it is an array of ids or just one single id.
     *
     * @author RickG
     */

    public function __construct() {
        parent::__construct();

        // Get the pagination request variables
		$mainframe = JFactory::getApplication() ;
		$limit = $mainframe->getUserStateFromRequest('global.list.limit', 'limit', $mainframe->getCfg('list_limit'), 'int');
		$limitstart = $mainframe->getUserStateFromRequest(JRequest::getVar('option').JRequest::getVar('view').'.limitstart', 'limitstart', 0, 'int');

		// Set the state pagination variables
		$this->setState('limit', $limit);
		$this->setState('limitstart', $limitstart);

        // Get the category id or array of ids.
		$idArray = JRequest::getVar('cid',  0, '', 'array');
    	$this->setId( (int)$idArray[0] );
    }



	 /**
     * Resets the category id and data
     *
     * @author RickG
     */
    public function setId($id)
    {
        $this->_id = $id;
        $this->_data = null;
    }

	/**
	 * Gets the total number of categories
	 *
     * @author RickG, jseros
	 * @return int Total number of categories in the database
	 */
	public function _getTotal(){
    	if (empty($this->_total)) {
			$query = 'SELECT `category_id` FROM `#__vm_category`';
			$this->_total = $this->_getListCount($query);
        }
        return $this->_total;
    }

   	/**
	 * Loads the pagination for the category table
	 *
     * @author RickG, jseros
     * @return JPagination Pagination for the current list of categories
	 */
    public function getPagination(){
		if (empty($this->_pagination)) {
			jimport('joomla.html.pagination');
			$this->_pagination = new JPagination($this->_getTotal(), $this->getState('limitstart'), $this->getState('limit'));
		}
		return $this->_pagination;
	}

    /**
     * Retrieve the detail record for the current $id if the data has not already been loaded.
     *
     * @author RickG, jseros, RolandD, Max Milbers
     */
	public function getCategory($category_id=0,$childs=TRUE){

		if(!empty($category_id)) $this->setId((int)$category_id);

  		if (empty($this->_data)) {
   			$this->_data = $this->getTable();
   			$this->_data->load((int)$this->_id);
  		}

  		if($childs){
  			$this->_data->haschildren = $this->hasChildren($this->_id);

			/* Get the product count */
			$this->_data->productcount = $this->countProducts($this->_id);

			/* Get parent for breatcrumb */
			$this->_data->parents = $this->getparentsList($this->_id);
  			
  			/* Get children if they exist */
			$child_cats = array();
			$child_tables = array();
			$child_cats = $this->getChildrenList($this->_id);
			$i=0;
			foreach ( $child_cats as $cat ) {
				$table = $this->getTable();
				$table->load((int)$cat->category_id);
				$child_tables[$i] = $table;	
				$i++;			
			}
			$this->_data->children = $child_tables;
//			if ($this->_data->haschildren) $this->_data->children = $this->getChildrenList($this->_id);
//			else $this->_data->children = null;
			


  		}

  		return $this->_data;

	}

//	/**
//	* Load a category and it's details
//	*
//	* @author RolandD
//	* @return object containing the category
//	*/
//	public function getCategory($category_id=0) {
//		$db = JFactory::getDBO();
//		$row =& $this->getTable('category');
//		$row->load($category_id);
//		if (VmConfig::get('showCategory',1)) {
//		/* Check for children */
//		$row->haschildren = $this->hasChildren($category_id);
//
//		/* Get children if they exist */
//		if ($row->haschildren) $row->children = $this->getChildrenList($category_id);
//		else $row->children = null;
//
//		/* Get the product count */
//		$row->productcount = $this->getProductCount($category_id);
//		}
//		/* Get parent for breatcrumb */
//		$row->parents = $this->getparentsList($category_id);
//
//		return $row;
//	}

    /**
	 * Get the list of child categories for a given category
	 *
	 * @param int $category_id Category id to check for child categories
	 * @return object List of objects containing the child categories
	 */
	public function getChildCategoryList($vendorId, $category_id) {

		$query = 'SELECT `#__vm_category`.`category_id`,`#__vm_category`.`category_name`, ';
		$query .= '`#__vm_category`.`category_description`, ';
		$query .= '`#__vm_category`.`category_thumb_image`, ';
		$query .= '`#__vm_category`.`category_full_image` ';
		$query .= 'FROM `#__vm_category`, `#__vm_category_xref` ';
		$query .= 'WHERE `#__vm_category_xref`.`category_parent_id` = ' . $category_id . ' ';
		$query .= 'AND `#__vm_category`.`category_id` = `#__vm_category_xref`.`category_child_id` ';
		$query .= 'AND `#__vm_category`.`vendor_id` = ' . $vendorId . ' ';
		$query .= 'AND `#__vm_category`.`category_publish` = "Y" ';
		$query .= 'ORDER BY `#__vm_category`.`list_order`, `#__vm_category`.`category_name` ASC';
		$childList = $this->_getList( $query ); 
		
		return $childList;
	}


	/**
	* Return an array containing category information
	*
	* @author
	*
	* @param boolean $onlyPublished Show only published categories?
	* @param boolean $withParentId Keep in mind $parentId param?
	* @param integer $parentId Show only its childs
	* @param string $keyword the keyword to filter categories
	* @return array Categories list
	*/
	public function getCategoryTree($onlyPublished = true, $withParentId = false, $parentId = 0, $keyword = "") {

		$vendorId = 1;
		$categories = Array();

		$query = "SELECT c.category_id, c.category_description, c.category_name, c.ordering, c.published, cx.category_child_id, cx.category_parent_id, cx.category_shared
				  FROM #__vm_category c
				  LEFT JOIN #__vm_category_xref cx
				  ON c.category_id = cx.category_child_id
				  WHERE 1 ";

		// Get only published categories
		if( $onlyPublished ) {
			$query .= "AND c.published = 1 ";
		}

		if( !empty( $keyword ) ) {
			$query .= "AND ( c.category_name LIKE '%".$keyword."%'
					   OR c.category_description LIKE '%".$keyword."%') ";
		}

		if( $withParentId ){
			$query .= " AND cx.category_parent_id = ". $this->_db->Quote($parentId);
		}
		if(!class_exists('Permissions')) require(JPATH_VM_ADMINISTRATOR.DS.'helpers'.DS.'permissions.php');
		if( !Permissions::getInstance()->check('admin') ){
			$query .= " AND (c.vendor_id = ". $this->_db->Quote($vendorId) . " OR cx.category_shared = '1') ";
		}

		$filterOrder = JRequest::getCmd('filter_order', 'c.ordering');
		$filterOrderDir = JRequest::getCmd('filter_order_Dir', 'ASC');
		// $filterOrder can still be empty at this point!
		if( empty( $filterOrder )) {
			$filterOrder = 'c.ordering';
		}
		if( empty( $filterOrderDir )) {
			$filterOrderDir = 'ASC';
		}
		$query .= " ORDER BY ". $filterOrder ." ". $filterOrderDir;

		// Set the query in the database connector
		$this->_db->setQuery($query);

		// Transfer the Result into a searchable Array
		$categories = $this->_db->loadObjectList();

		return $categories;

	}


	/**
	 * Sorts an array with categories so the order of the categories is the same as in a tree.
	 *
	 * @author jseros
	 *
	 * @param array $categoryArr
	 * @return associative array ordering categories
	 */
	public function sortCategoryTree($categoryArr){

		/** FIRST STEP
	    * Order the Category Array and build a Tree of it
	    **/
		$idList = array();
		$rowList = array();
		$depthList = array();

		$children = array();
		$parentIds = array();
		$parentIdsHash = array();
		$parentId = 0;

		for( $i = 0, $nrows = count($categoryArr); $i < $nrows; $i++ ) {
			$parentIds[$i] = $categoryArr[$i]->category_parent_id;

			if($categoryArr[$i]->category_parent_id == 0){
				array_push($idList, $categoryArr[$i]->category_child_id);
				array_push($rowList, $i);
				array_push($depthList, 0);
			}

			$parentId = $parentIds[$i];

			if( isset($parentIdsHash[$parentId] )){
				$parentIdsHash[$parentId][$categoryArr[$i]->category_child_id] = $i;
			}
			else{
				$parentIdsHash[$parentId] = array($categoryArr[$i]->category_child_id => $i);
			}

		}

		$loopCount = 0;
		$watch = array(); // Hash to store children

		while( count($idList) < $nrows ){
			if( $loopCount > $nrows ) break;

			$idTemp = array();
			$rowTemp = array();
			$depthTemp = array();

			for($i = 0, $cIdlist = count($idList); $i < $cIdlist ; $i++) {
				$id = $idList[$i];
				$row = $rowList[$i];
				$depth = $depthList[$i];

				array_push($idTemp, $id);
				array_push($rowTemp, $row);
				array_push($depthTemp, $depth);

				$children = @$parentIdsHash[$id];

				if( !empty($children) ){
					foreach($children as $key => $value) {

						if( !isset($watch[$id][$key]) ){
							$watch[$id][$key] = 1;
							array_push($idTemp, $key);
							array_push($rowTemp, $value);
							array_push($depthTemp, $depth + 1);
						}
					}
				}
			}
			$idList = $idTemp;
			$rowList = $rowTemp;
			$depthList = $depthTemp;
			$loopCount++;
		}

		return array('id_list' => $idList,
					 'row_list' => $rowList,
					 'depth_list' => $depthList,
					 'categories' => $categoryArr
		);
	}


	/**
	* count the products in a category
	*
	* @author RolandD, Max Milbers
	* @return array list of categories product is in
	*/
	public function countProducts($cat_id=0) {

		if(!empty($this->_db))$this->_db = JFactory::getDBO();
		$vendorId = 1;
		if ($cat_id > 0) {
			$q = 'SELECT count(#__vm_product.product_id) AS total
			FROM #__vm_product, #__vm_product_category_xref
			WHERE #__vm_product.vendor_id = "'.$vendorId.'"
			AND #__vm_product_category_xref.category_id = '.$this->_db->Quote($cat_id).'
			AND #__vm_product.product_id = #__vm_product_category_xref.product_id
			AND #__vm_product.published = "1" ';
			$this->_db->setQuery($q);
			$count = $this->_db->loadResult();
		}

		return $count;
	}

	/**
	 * NOT USED,
	 * Function to calculate and return the number of products in category $category_id
	 * @author RolandD
	 *
	 * @todo Add vendor
	 * @param int $category_id the category ID to count products for
	 * @return int the number of products found
	 */
//	public function getProductCount($category_id) {
//		$db = JFactory::getDBO();
//		$q = "SELECT count(#__vm_product.product_id) AS num_rows
//			FROM #__vm_product, #__vm_product_category_xref, #__vm_category
//			WHERE #__vm_product.vendor_id = 1
//			AND #__vm_product_category_xref.category_id = ".$category_id."
//			AND #__vm_category.category_id = #__vm_product_category_xref.category_id
//			AND #__vm_product.product_id = #__vm_product_category_xref.product_id
//			AND #__vm_product.published = 1";
//			if (VmConfig::get('check_stock') && VmConfig::get('pshop_show_out_of_stock_products') != "1") {
//				$q .= " AND product_in_stock > 0 ";
//			}
//		$db->setQuery($q);
//		return $db->loadResult();
//	}


    /**
	 * Order any category
	 *
     * @author jseros
     * @param  int $id category id
     * @param  int $movement movement number
	 * @return bool
	 */
	public function orderCategory($id, $movement){
		//retrieving the category table object
		//and loading data
		$row = $this->getTable();
		$row->load($id);

		$query = 'SELECT category_parent_id FROM #__vm_category_xref WHERE category_child_id = '. $this->_db->Quote( $row->category_id );
		$this->_db->setQuery($query);
		$parent = $this->_db->loadObject();

		if (!$row->move( $movement, $parent->category_parent_id)) {
			$this->setError($row->getError());
			return false;
		}

		return true;
	}


	/**
	 * Order category group
	 *
     * @author jseros
     * @param  array $cats categories to order
	 * @return bool
	 */
	public function setOrder($cats){
		$total		= count( $cats );
		$groupings	= array();
		$row = $this->gettable();

		$order		= JRequest::getVar('order', array(), 'post', 'array');
		JArrayHelper::toInteger($order);

		$query = 'SELECT category_parent_id FROM #__vm_category
				  LEFT JOIN #__vm_category_xref cx
				  ON c.category_id = cx.category_child_id
			      WHERE c.category_id = %s';

		// update ordering values
		for( $i=0; $i < $total; $i++ ) {

			$row->load( $cats[$i] );
			$this->_db->setQuery( sprintf($query, $this->_db->Quote( $cats[$i] )), 0 ,1 );
			$parent = $this->_db->loadObject();

			$groupings[] = $parent->category_parent_id;
			if ($row->ordering != $order[$i]) {
				$row->ordering = $order[$i];
				if (!$row->store()) {
					$this->setError($row->getError());
					return false;
				}
			}
		}

		// execute reorder for each parent group
		$groupings = array_unique( $groupings );
		foreach ($groupings as $group){
			$row->reorder($group);
		}

		return true;
	}


    /**
     * Retrieve the detail record for the parent category of $categoryd
     *
     * @author jseros
     *
     * @param int $categoryId Child category id
     * @return JTable parent category data
     */
	public function getParentCategory( $categoryId = 0 ){
		$data = $this->getRelationInfo( $categoryId );
		$parentId = isset($data->category_parent_id) ? $data->category_parent_id : 0;

     	$parent = $this->getTable();
  		$parent->load((int) $parentId);

  		return $parent;
	}


    /**
     * Retrieve category child-parent relation record
     *
     * @author jseros
     *
     * @param int $category_id
     * @return object Record of parent relation
     */
    public function getRelationInfo( $category_id = 0 ){
    	$category_id = (int) $category_id;

    	$query = 'SELECT category_parent_id, category_shared, category_list
    			  FROM #__vm_category_xref
    			  WHERE category_child_id = '. $this->_db->Quote($category_id);
    	$this->_db->setQuery($query);

    	return $this->_db->loadObject();
    }

	/* array container for category tree ID*/
	var $container = array();


	/**
	* Checks for children of the category $category_id
	*
	* @author RolandD
	* @param int $category_id the category ID to check
	* @return boolean true when the category has childs, false when not
	*/
	public function hasChildren($category_id) {
		$db = JFactory::getDBO();
		$q = "SELECT category_child_id
			FROM #__vm_category_xref
			WHERE category_parent_id = ".$category_id;
		$db->setQuery($q);
		$db->query();
		if ($db->getAffectedRows() > 0) return true;
		else return false;
	}

	/**
	 * Creates a bulleted of the childen of this category if they exist
	 *
	 * @author RolandD
	 * @todo Add vendor ID
	 * @param int $category_id the category ID to create the list of
	 * @return array containing the child categories
	 */
	public function getChildrenList($category_id,$limit=false) {
		$db = JFactory::getDBO();
		$childs = array();

		$q = "SELECT category_id, category_child_id, category_name
			FROM #__vm_category, #__vm_category_xref
			WHERE #__vm_category_xref.category_parent_id = ".$category_id."
			AND #__vm_category.category_id=#__vm_category_xref.category_child_id
			AND #__vm_category.vendor_id = 1
			AND #__vm_category.category_publish = 'Y'
			ORDER BY #__vm_category.list_order, #__vm_category.category_name ASC";
		if ($limit) $q .=' limit 0,'.$limit;
		$db->setQuery($q);
		$childs = $db->loadObjectList();

		/* Get the products in the category */
		if(!empty($childs)){
			foreach ($childs as $ckey => $child) {
				$childs[$ckey]->number_of_products = $this->countProducts($child->category_child_id);
			}
		}


		return $childs;
	}

	/**
	 * Creates a bulleted of the childen of this category if they exist
	 *
	 * @author RolandD
	 * @todo Add vendor ID
	 * @param int $category_id the category ID to create the list of
	 * @return array containing the child categories
	 */
	public function getparentsList($category_id) {

		$db = & JFactory::getDBO();
		$menu = &JSite::getMenu();
		$parents = array();
		if (empty($query['Itemid'])) {
			$menuItem = &$menu->getActive();
		} else {
			$menuItem = &$menu->getItem($query['Itemid']);
		}
		$menuCatid = (empty($menuItem->query['category_id'])) ? 0 : $menuItem->query['category_id'];
		if ($menuCatid == $category_id) return ;
		$parents_id = array_reverse($this->getCategoryRecurse($category_id,$menuCatid));
		foreach ($parents_id as $id ) {
			$q = "SELECT `category_name`,`category_id`
				FROM  `#__vm_category`
				WHERE  `category_id`=".$id;

			$db->setQuery($q);

			$parents[] = $db->loadObject();
		}
		return $parents;
	}

	function getCategoryRecurse($category_id,$catMenuId,$first=true ) {
		static $idsArr = array();
		if($first) {
			$idsArr = array();
		}

		$db = & JFactory::getDBO();
		$q  = "SELECT `category_child_id` AS `child`, `category_parent_id` AS `parent`
			FROM  #__vm_category_xref AS `xref`
			WHERE `xref`.`category_child_id`= ".$category_id;
		$db->setQuery($q);
		if (!$ids = $db->loadObject()) {
			return $idsArr;
		}
		if ($ids->child) $idsArr[] = $ids->child;
		if($ids->child != 0 and $catMenuId != $category_id and $catMenuId != $ids->parent) {
			$this->getCategoryRecurse($ids->parent,$catMenuId,false);
		}
		return $idsArr;
	}

	/*
	* Returns an array of the categories recursively for a given category
	* @author Kohl Patrick
	* @param int $id
	* @param int $maxLevel
	 * @Object $this->container
	*/
	function treeCat($id=0,$maxLevel =1000) {
		static $level = 0;
		static $num = -1 ;
		$db = & JFactory::getDBO();
		$q = 'SELECT category_child_id,category_name FROM #__vm_category_xref
		LEFT JOIN #__vm_category on #__vm_category.category_id=#__vm_category_xref.category_child_id
		WHERE category_parent_id='.$id;
		$db->setQuery($q);
		$num ++;
		// if it is a leaf (no data underneath it) then return
		$childs = $db->loadObjectList();
		if ($level==$maxLevel) return;
		if ($childs) {
			$level++;
			foreach ($childs as $child) {
				$this->container[$num]->id = $child->category_child_id;
				$this->container[$num]->name = $child->category_name;
				$this->container[$num]->level = $level;
				self::treeCat($child->category_child_id,$maxLevel );
			}
			$level--;
		}
	}
	/**
	 * @author Kohl Patrick
	 * @param  $maxlevel the number of level
	 * @param  $id the root category id
 	 * @Object $this->container
	 * @ return categories id, name and level in container
	 * if you set Maxlevel to 0, then you see nothing
	 * max level =1 for simple category,2 for category and child cat ....
	 * don't set it for all (1000 levels)
	 */
	function GetTreeCat($id=0,$maxLevel = 1000) {
		self::treeCat($id ,$maxLevel) ;
		return $this->container ;
	}

}